﻿using QueuesLib;
using WorkStation.QueuesService;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Threading;

namespace WorkStation {
    /// <summary>
    /// Interaction logic for WorkStationWindow.xaml
    /// </summary>
    public partial class WorkStationWindow : Window, NotificationsObserver {
        private WorkStationNotifService notifService;
        private delegate void NoArgsDelegate(); //useful to schedule tasks in UI thread from other threads
        private Dictionary<int, Label> labelsIdx = new Dictionary<int, Label>(4);
        private Dictionary<int, Button> buttonsIdx = new Dictionary<int, Button>(4);
        private List<Button> subjectButtons = new List<Button>();
        private List<Label> countLabels = new List<Label>();
        private Ticket currentTicket;
        private PreloaderWindow preloader;

        public bool QueuesInitialized { get; private set; }

        public string WorkStationId {
            get {
                return ConfigurationManager.AppSettings.Get("WorkStationId");
            }
        }

        public string WorkStationName {
            get {
                return ConfigurationManager.AppSettings.Get("WorkStationName");
            }
        }

        public int MaxTries {
            get {
                return Convert.ToInt32(ConfigurationManager.AppSettings.Get("MaxTries"));
            }
        }

        public int WaitingMillis {
            get {
                return Convert.ToInt32(ConfigurationManager.AppSettings.Get("WaitingMillis"));
            }
        }

        public string ServerAddress {
            get {
                return ConfigurationManager.AppSettings.Get("ServerAddress");
            }
        }

        public void ShowInfoMessage(string caption, string message) {
            MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Information);
        }

        public WorkStationWindow() {
            preloader = new PreloaderWindow();
            preloader.Show();

            InitializeComponent();
            title.Text = "Posto de Trabalho " + WorkStationName;
            
            this.Visibility = System.Windows.Visibility.Hidden;
            //ContentGrid.Visibility = System.Windows.Visibility.Hidden;
            QueuesInitialized = false;
            
            NoArgsDelegate services = new NoArgsDelegate(StartBackgroundServices);
            services.BeginInvoke(null, null);
            //StartBackgroundServices();
        }

        private void StartBackgroundServices() {
            preloader.ShowInfo("A iniciar serviços de rede...");
            notifService = new WorkStationNotifService();
            notifService.AddObserver(this);
            notifService.Start();
            Console.WriteLine("Started");

            preloader.ShowInfo("A contactar o servidor...");
            int tries = 0;
            while (tries < MaxTries) {
                var client = new WorkStationServiceClient("WorkStation", ServerAddress);
                try {
                    client.Register(WorkStationId);
                    client.Close();
                    return;
                }
                catch (FaultException<UnauthorizedAccessException>) {
                    preloader.ShowInfo("Erro com o identificador do Posto de Trabalho.\nContacte o administrador.");
                    client.Abort();
                    return;
                }
                catch (FaultException f) {
                    Console.WriteLine(f.Message);
                    client.Abort();
                }
                catch (CommunicationException c) {
                    Console.WriteLine(c.Message);
                    client.Abort();
                }
                catch (TimeoutException) {
                    Console.WriteLine("Timeout");
                    client.Abort();
                }
                Thread.Sleep(WaitingMillis);
                preloader.ShowInfo(String.Format("Contactando o servidor...\nTentativa {0} de {1}.", tries + 1, MaxTries));
                tries++;
            }
            preloader.ShowInfo("Não foi possível contactar o servidor.\nPor favor tente mais tarde,\ne caso o problema persista contate o administrador.");
            //Close();

        }

        //Event declared in XAML - Loaded
        private void WindowLoaded(object sender, RoutedEventArgs e) {
            subjectButtons.Add(buttonA);
            subjectButtons.Add(buttonB);
            subjectButtons.Add(buttonC);
            subjectButtons.Add(buttonD);

            countLabels.Add(labelCountA);
            countLabels.Add(labelCountB);
            countLabels.Add(labelCountC);
            countLabels.Add(labelCountD);
        }

        //Event declared in XAML - Closing
        private void StopService(object sender, System.ComponentModel.CancelEventArgs e) {
            Console.WriteLine("Closing...");
            notifService.Stop();
            try {
                var client = new WorkStationServiceClient("WorkStation", ServerAddress);
                client.UnregisterAsync(WorkStationId);
                client.Close();
            }
            catch (CommunicationException c) {
                Console.Error.WriteLine(c.Message);
            }
            catch (TimeoutException) {
                // não respondeu, paciência...
            }
        }

        //Event declared in XAML
        private void ButtonNextGeneral_Click(object sender, RoutedEventArgs e) {
            Button btn = (Button) sender;
            btn.IsEnabled = false;
            NoArgsDelegate tk = () => {
                GetTicket(Subject.ANY_SUBJECT);
            };
            tk.BeginInvoke(null, null);
            btn.IsEnabled = true;   
        }

        //Event declared in XAML
        private void BtnNext_Click(object sender, RoutedEventArgs e) {
            Button btn = (Button) sender;

            if (btn.Tag != null) {
                btn.IsEnabled = false;
                int subjectId = (int) btn.Tag;
                NoArgsDelegate tk = () => {
                    GetTicket(subjectId);
                };
                tk.BeginInvoke(null, null);
                btn.IsEnabled = true;
            }
        }

        private void GetTicket(int subjectId) {
            var client = new WorkStationServiceClient("WorkStation", ServerAddress);
            currentTicket = null;
            try {
                if (subjectId == Subject.ANY_SUBJECT) {
                    currentTicket = client.GetNextTicketInGeneral(WorkStationId);
                } else {
                    currentTicket = client.GetNextTicketBySubject(WorkStationId, subjectId);
                }
                client.Close();
            }
            catch (FaultException<InvalidOperationException>) {
                Console.WriteLine("Fila de espera vazia");
                client.Abort();
            }
            catch (FaultException f) {
                Console.WriteLine(f.Message);
                client.Abort();
            }
            catch (CommunicationException c) {
                Console.WriteLine(c.Message);
                client.Abort();
            }
            catch (TimeoutException) {
                Console.WriteLine("Servidor indisponível. Aguarde um pouco.");
                client.Abort();
            };
            NoArgsDelegate ticketText = () => {
                labelCurrentTicket.Content = currentTicket == null ? "" : currentTicket.Text;
            };

            labelCurrentTicket.Dispatcher.BeginInvoke(ticketText);
        }

        public void Initialize(List<Subject> subjects, List<Counter> counters, List<QueueInfo> tickets) {
            if (subjects.Count == 0) {
                throw new NotInitializedException("Subjects list is empty");
            }

            subjects.Sort((a, b) => (a.Mnemonic.CompareTo(b.Mnemonic)));

            int i;
            for (i = 0; i < subjects.Count; i++) {
                labelsIdx[subjects[i].Id] = countLabels[i];
                buttonsIdx[subjects[i].Id] = subjectButtons[i];
            }

            NoArgsDelegate tags = () => {
                for (i = 0; i < subjects.Count; i++) {
                    subjectButtons[i].Tag = subjects[i].Id;
                    subjectButtons[i].ToolTip = subjects[i].Name;
                    countLabels[i].ToolTip = "Utentes em espera para " + subjects[i].Name;
                }

                //hide the useless buttons and labels
                while (i < subjectButtons.Count) {
                    subjectButtons[i].Visibility = System.Windows.Visibility.Collapsed;
                    countLabels[i].Visibility = System.Windows.Visibility.Collapsed;
                    i++;
                }

                var info = tickets.Find(t => t.WorkStationId == this.WorkStationId);
                labelCurrentTicket.Content = info != null ? info.TicketText : "";

                //show interface ready to work with
                
                this.Visibility = System.Windows.Visibility.Visible;
                System.Drawing.Rectangle wa = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
                this.Left = wa.Width - ContentGrid.ActualWidth;
            };

            try {
                SetCounters(counters);
                labelCountTotal.Dispatcher.BeginInvoke(tags);
                QueuesInitialized = true;
                NoArgsDelegate c = () => preloader.Close();
                preloader.Dispatcher.BeginInvoke(c);
                this.Visibility = System.Windows.Visibility.Visible;
            }
            catch (Exception e) {
                throw new NotInitializedException("Error during initialization", e);
            }
        }

        private void SetCounters(List<Counter> counters) {
            int totalTickets = 0;
            NoArgsDelegate buttonTasks = () => { };
            foreach (var item in counters) {
                if (!buttonsIdx.ContainsKey(item.SubjectId)) {
                    throw new UnknownSubjectException("Unknown subject");
                }

                totalTickets += item.Count;
                var bt = buttonsIdx[item.SubjectId];
                buttonTasks += () => { bt.IsEnabled = item.Count > 0; };
            }
            NoArgsDelegate totals = () => {
                labelCountTotal.Content = totalTickets;
                buttonNextGeneral.IsEnabled = (totalTickets > 0);
                foreach (var c in counters) {
                    labelsIdx[c.SubjectId].Content = c.Count;
                }
            };

            labelCountTotal.Dispatcher.BeginInvoke(buttonTasks + totals);
        }

        public void Update(List<Counter> counters, List<QueueInfo> Tickets) {
            if (!QueuesInitialized) {
                throw new NotInitializedException("Not initialized");
            }

            SetCounters(counters);
        }

        private void CloseButton_Click(object sender, RoutedEventArgs e) {
            this.Close();
            Application.Current.Shutdown();
        }

        private void MinButton_Click(object sender, RoutedEventArgs e) {
            this.WindowState = System.Windows.WindowState.Minimized;
        }

        private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e) {
            if (e.ChangedButton == MouseButton.Left) {
                this.DragMove();
            }
        }
    }
}
